package com.yc.cloud.portal.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yc.cloud.api.enums.CompanyStatusEnums;
import com.yc.cloud.api.service.MemberAccountCacheService;
import com.yc.cloud.api.service.WxCacheService;
import com.yc.cloud.basic.config.PathUtils;
import com.yc.cloud.basic.constants.GlobalConstants;
import com.yc.cloud.basic.constants.RedisKeyConstants;
import com.yc.cloud.basic.constants.TipsConstants;
import com.yc.cloud.basic.dto.BaseQueryParam;
import com.yc.cloud.basic.exception.Asserts;
import com.yc.cloud.basic.utils.SnowflakeIdGenerator;
import com.yc.cloud.entity.Company;
import com.yc.cloud.entity.MemberAccount;
import com.yc.cloud.mapper.MemberAccountMapper;
import com.yc.cloud.oss.service.OssFileService;
import com.yc.cloud.portal.common.PortalErrorTips;
import com.yc.cloud.portal.dto.request.MemberAccountRegisterParam;
import com.yc.cloud.portal.dto.request.MemberAccountUpdateParam;
import com.yc.cloud.portal.dto.request.MemberAccountUpdatePwdParam;
import com.yc.cloud.portal.service.CompanyService;
import com.yc.cloud.portal.service.MemberAccountService;
import com.yc.cloud.portal.service.MemberLoginLogService;
import com.yc.cloud.portal.vo.MemberAccountVo;
import com.yc.cloud.security.dto.MemberUserDetails;
import com.yc.cloud.security.utils.JwtTokenUtil;
import com.yc.cloud.security.utils.MemberUtils;
import com.yc.cloud.wechat.common.WeChatTips;
import com.yc.cloud.wechat.dto.QRCodeDto;
import com.yc.cloud.wechat.dto.WxAccessTokenDto;
import com.yc.cloud.wechat.dto.request.WxAuthorizeParam;
import com.yc.cloud.wechat.dto.request.WxBarCodeCreateParam;
import com.yc.cloud.wechat.enums.WxGrantTypeEnums;
import com.yc.cloud.wechat.service.WxService;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Yancy
 * @since 2023-12-23
 */
@Service
@Slf4j
public class MemberAccountServiceImpl extends ServiceImpl<MemberAccountMapper, MemberAccount> implements MemberAccountService {
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private MemberAccountMapper memberAccountMapper;

    @Resource
    private MemberAccountCacheService memberAccountCacheService;
    @Resource
    private OssFileService ossFileService;
    @Resource
    private WxCacheService wxCacheService;

    @Resource
    private MemberLoginLogService memberLoginLogService;

    @Resource
    @Qualifier("wxFuWuHaoServiceImpl")
    private WxService wxFuWuHaoService;

    @Resource
    @Qualifier("wxMiniProgramServiceImpl")
    private WxService wxMiniProgramService;

    @Resource
    @Qualifier("wxSubscriptionServiceImpl")
    private WxService wxSubscriptionService;

    private SnowflakeIdGenerator snowflakeIdGenerator;

    // 在适当的地方定义一个 Map
    private final Map<String, WxService> wxServiceMap = new HashMap<>();

    @Resource
    private CompanyService companyService;

    //TODO 根据项目来设定
    String appid = "wx0c2b01216bbe0d21";
    String appKey = "847ae7ed2e42498b9f34f280b3af75d6";

    @PostConstruct
    public void init() {
        // 在初始化时将不同的 WxGrantTypeEnums 和对应的 WxService 放入 Map 中
        wxServiceMap.put(WxGrantTypeEnums.MINI_PROGRAM.getCode(), wxMiniProgramService);
        wxServiceMap.put(WxGrantTypeEnums.SERVICE_ACCOUNT.getCode(), wxFuWuHaoService);
        wxServiceMap.put(WxGrantTypeEnums.SUBSCRIPTION_ACCOUNT.getCode(), wxSubscriptionService); // Assuming there is a default case
        snowflakeIdGenerator = new SnowflakeIdGenerator(1L);
    }

    @Override
    public IPage<MemberAccountVo> list(BaseQueryParam queryParam) {
        val query = new QueryWrapper<MemberAccount>();
        IPage<MemberAccount> page;
        //排序
        if (StrUtil.isNotEmpty(queryParam.getSortBy())) {
            if (BaseQueryParam.ORDER_DESC.equals(queryParam.getOrder())) {
                query.orderByDesc(MemberAccount.ID);
            } else if (BaseQueryParam.ORDER_ASC.equals(queryParam.getOrder())) {
                query.orderByAsc(MemberAccount.ID);
            }
        }
        //模糊查询
//        if (StrUtil.isNotEmpty(queryParam.getKeyword())) {
//            query.and(queryWrapper -> {
//
//            });
//        }
        //分页
        if (queryParam.getPage()) {
            page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize(), true);
        } else {
            page = new Page<>(queryParam.getPageNum(), memberAccountMapper.selectCount(query), true);
        }
        val result = memberAccountMapper.selectPage(page, query);
        return result.convert(this::convertVo);
    }

    @Override
    public MemberUserDetails loadUserByUsername(String username) {
        //获取用户信息
        val member = getByUserName(username);
        if (member != null) {
            return new MemberUserDetails(member);
        }
        throw new UsernameNotFoundException(TipsConstants.USERNAME_NOT_MATCH_PASSWORD);
    }

    public MemberUserDetails loadUserByOpenId(String openId) {
        //获取用户信息
        val member = baseMapper.selectOne(new QueryWrapper<MemberAccount>().eq(MemberAccount.OPEN_ID, openId));
        if (member != null) {
            return new MemberUserDetails(member);
        }
        return null;
    }

    @Override
    public String login(String username, String password) {
        String token = null;
        //密码需要客户端加密后传递
        try {
            memberAccountCacheService.delByUserName(username);
            val memberUserDetails = loadUserByUsername(username);
            if (!passwordEncoder.matches(password, memberUserDetails.getPassword())) {
                Asserts.fail("密码不正确");
            }
            if (!memberUserDetails.isEnabled()) {
                Asserts.fail("帐号已被禁用");
            }
            token = getAccessToken(memberUserDetails);
            //更新登录记录
            updateLoginInfo(memberUserDetails.getMemberAccount());
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
        }
        return token;
    }

    private String getAccessToken(MemberUserDetails memberUserDetails) {
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(memberUserDetails, null, memberUserDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return jwtTokenUtil.generateToken(memberUserDetails);
    }

    private void updateLoginInfo(MemberAccount memberAccount) {
        val memberId = memberAccount.getId();
        //更新用户最后登录时间
        updateLoginTime(memberId);
        //添加用户登录记录
        memberLoginLogService.saveMemberLoginLog(memberId);
    }


    @Override
    public String registerOrLoginWithWeChat(WxAuthorizeParam param) {
        //小程序方式获取openid
        val code = param.getCode();
        val wxService = wxServiceMap.get(param.getGrantType());
        //TODO 根据项目来设定
        val wxAccessTokenDto = wxService.getAccessToken(code, appid, appKey);
        if (wxAccessTokenDto == null || StrUtil.isEmpty(wxAccessTokenDto.getAccessToken())) {
            Asserts.fail(WeChatTips.AUTHORIZE_CODE_ERROR);
        }
        var accessToken = wxAccessTokenDto.getAccessToken();
        String openId = wxService.getOpenId(code, appid, appKey);
        var memberUserDetails = loadUserByOpenId(openId);
        //如果是第一次登录，进行信息添加
        if (memberUserDetails == null || memberUserDetails.getMemberAccount() == null) {
            val member = new MemberAccount();
            val wxUserInfo = wxService.getUserInfo(accessToken, openId);
            if (wxUserInfo != null) {
                member.setTrueName(wxUserInfo.getNickname());
            } else {
                //如果获取不到用户信息,则系统自己添加一个默认的名字
                member.setTrueName("用户" + snowflakeIdGenerator.generateId());
            }
            member.setOpenId(openId);
            member.setStatus(GlobalConstants.STATUS_ENABLE);
            member.setUsername(openId);
            member.setLoginTime(LocalDateTime.now());
            //TODO 后续有待优化
            String MEMBER_DEFAULT_PASSWORD = "dc483e80a7a0bd9ef71d8cf973673924";
            member.setPassword(passwordEncoder.encode(MEMBER_DEFAULT_PASSWORD));
            val result = save(member);
            if (!result) {
                return null;
            }
            memberUserDetails = new MemberUserDetails(member);
        }
        val token = getAccessToken(memberUserDetails);
        val wxInfoKey = RedisKeyConstants.WX_INFO + openId;
        //微信access_token有效期7200秒
        wxCacheService.setByKey(wxInfoKey, JSONUtil.toJsonStr(wxAccessTokenDto), 7200L);
        val memberAccount = memberUserDetails.getMemberAccount();
        //目前只支持小程序方式生成用户二维码
        if (WxGrantTypeEnums.MINI_PROGRAM.getCode().equals(param.getGrantType()) && StrUtil.isEmpty(memberAccount.getQrCode())) {
            val barCodeCreateParam = new WxBarCodeCreateParam();
            barCodeCreateParam.setGrantType(param.getGrantType());
            createQrCode(barCodeCreateParam);
        }
        //TODO 可以使用线程方式去更新数据
        updateLoginInfo(memberAccount);
        return token;
    }

    @Override
    public MemberAccount getByUserName(String username) {
        var memberAccount = getByUserNameFromCache(username);
        if (memberAccount != null) {
            return memberAccount;
        }
        memberAccount = getByUserNameFromDB(username);
        if (memberAccount != null) {
            //以用户名作为key名称
            val key = RedisKeyConstants.MEMBER_ACCOUNT + username;
            memberAccountCacheService.setByKey(key, memberAccount);
            return memberAccount;
        }
        return null;
    }

    @Override
    public MemberAccount register(MemberAccountRegisterParam param) {
        //检查用户名是否已存在
        if (getByUserName(param.getUsername()) != null) {
            Asserts.fail(TipsConstants.USERNAME_EXIST);
        }
        val memberAccount = registerMemberInfo(param);
        if (memberAccount == null) {
            Asserts.fail(PortalErrorTips.MEMBER_ACCOUNT_REGISTER_FAILED);
        }
        val company = registerCompanyInfo(memberAccount.getId(), param);
        if (company == null) {
            Asserts.fail(PortalErrorTips.COMPANY_INFO_REGISTER_FAILED);
        }
        //更新会员账号企业ID
        memberAccount.setCompanyId(company.getId());
        memberAccount.setCreateBy(memberAccount.getId());
        val updateFlag = updateById(memberAccount);
        return updateFlag ? memberAccount : null;
    }

    @Override
    public boolean update(Long memberId, MemberAccountUpdateParam param) {
        val memberAccount = getById(memberId);
        if (memberAccount != null) {
            BeanUtils.copyProperties(param, memberAccount);
            val updateFlag = updateById(memberAccount);
            if (updateFlag) {
                memberAccountCacheService.delByUserName(memberAccount.getUsername());
            }
            return updateFlag;
        }
        return false;
    }

    @Override
    public boolean addMemberVipExpireTime(Long memberId, int month, int day) {
        val memberAccount = getById(memberId);
        if (memberAccount == null) {
            return false;
        }
        memberAccount.setVip(true);
        val now = LocalDateTime.now();
        var vipExpireTime = memberAccount.getVipExpireTime();
        //没有开通过vip或者 vip已过期,则按照当前日期往后添加
        if (vipExpireTime == null || vipExpireTime.isBefore(now)) {
            vipExpireTime = now;
        }
        if (month != 0) {
            vipExpireTime = vipExpireTime.plusMonths(month);
        }
        if (day != 0) {
            vipExpireTime = vipExpireTime.plusDays(day);
        }
        memberAccount.setVipExpireTime(vipExpireTime);
        memberAccount.setUpdateBy(memberId);
        return updateById(memberAccount);
    }

    @Override
    public String createQrCode(WxBarCodeCreateParam param) {
        //有二维码了就不重复生成了
        val memberAccount = getById(MemberUtils.getCurrentUseId());
        val wxService = wxServiceMap.get(param.getGrantType());
        val key = RedisKeyConstants.WX_INFO + MemberUtils.getCurrentUserName();
        //先看redis里是否有该用户的access_token
        val wxInfo = wxCacheService.getByKey(key);
        WxAccessTokenDto wxAccessTokenDto;
        String wxAccessToken = "";
        if (StrUtil.isNotEmpty(wxInfo)) {
            wxAccessTokenDto = JSONUtil.toBean(wxInfo, WxAccessTokenDto.class);
        } else {
            wxAccessTokenDto = wxService.getAccessToken(null, appid, appKey);
            //access_token超时为7200秒,提前获取
            wxCacheService.setByKey(key, JSONUtil.toJsonStr(wxAccessTokenDto), 7000);
        }
        if (StrUtil.isNotEmpty(wxAccessTokenDto.getAccessToken())) {
            wxAccessToken = wxAccessTokenDto.getAccessToken();
        }
        val qrCodeDto = new QRCodeDto();
        qrCodeDto.setScene("shareId=" + MemberUtils.getCurrentUseId());
//        qrCodeDto.setEnvVersion("trial");
//        qrCodeDto.setCheckPath(false);
//        qrCodeDto.setPage("pages/index/index");
        val qrCodeData = wxService.getQRCode(wxAccessToken, qrCodeDto);
        val folder = Paths.get(PathUtils.getBasePath(), MemberUtils.getCurrentUserName());
        FileUtil.mkdir(folder);
        val path = Paths.get(folder.toString(), "qrcode.jpg").toString();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(qrCodeData)) {
            BufferedImage bi1 = ImageIO.read(byteArrayInputStream);
            File file = new File(path);
            ImageIO.write(bi1, "jpg", file);
            val ossFile = ossFileService.saveOssFile(path);
            //更新会员qrcode信息
            if (ossFile != null) {
                memberAccount.setQrCode(ossFile.getFileId());
                //更新用户信息
                updateById(memberAccount);
                return ossFileService.getOssFileUrl(ossFile.getFileId());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }


    @Override
    public MemberAccountVo convertVo(MemberAccount memberAccount) {
        if (memberAccount == null) {
            return null;
        }
        val vo = new MemberAccountVo();
        BeanUtils.copyProperties(memberAccount, vo);
        val iconUrl = ossFileService.getOssFileUrl(vo.getIcon());
        val qrCodeUrl = ossFileService.getOssFileUrl(vo.getQrCode());
        vo.setIconUrl(iconUrl);
        vo.setQrCodeUrl(qrCodeUrl);
        return vo;
    }

    @Override
    public boolean updatePwd(MemberAccountUpdatePwdParam param) {
        val memberAccount = getById(MemberUtils.getCurrentUseId());
        if (memberAccount == null) {
            return false;
        }
        //先检查旧密码是否匹配
//        val matchFlag = passwordEncoder.matches(param.getOldPassword(), memberAccount.getPassword());
//        if (!matchFlag) {
//            Asserts.fail(PortalErrorTips.MEMBER_PASSWORD_DIS_MATCH);
//        }
        memberAccount.setPassword(passwordEncoder.encode(param.getPassword()));
        val updateFlag = updateById(memberAccount);
        if (updateFlag) {
            memberAccountCacheService.delByUserName(memberAccount.getUsername());
        }
        return updateFlag;
    }


    public MemberAccount getByUserNameFromDB(String username) {
        return baseMapper.selectOne(new QueryWrapper<MemberAccount>().eq(MemberAccount.USERNAME, username));
    }

    public MemberAccount getByUserNameFromCache(String username) {
        val key = RedisKeyConstants.MEMBER_ACCOUNT + username;
        return memberAccountCacheService.getByKey(key);
    }

    private void updateLoginTime(Long memberId) {
        val memberAccount = getById(memberId);
        if (memberAccount != null) {
            memberAccount.setLoginTime(LocalDateTime.now());
            updateById(memberAccount);
        }
    }

    private MemberAccount registerMemberInfo(MemberAccountRegisterParam param) {
        val memberAccount = new MemberAccount();
        BeanUtils.copyProperties(param, memberAccount);
        memberAccount.setStatus(GlobalConstants.STATUS_ENABLE);
        memberAccount.setCreateTime(LocalDateTime.now());
        memberAccount.setCreateBy(0L);
        memberAccount.setUpdateBy(0L);
        //将密码进行加密操作
        String encodePassword = passwordEncoder.encode(param.getPassword());
        memberAccount.setPassword(encodePassword);
        val saveFlag = save(memberAccount);
        return saveFlag ? memberAccount : null;
    }

    private Company registerCompanyInfo(Long memberId, MemberAccountRegisterParam param) {
        val company = new Company();
        if (StrUtil.isNotEmpty(param.getCompanyName())) {
            company.setName(param.getCompanyName());
        } else {
            company.setName(param.getUsername() + "创建的企业");
        }
        company.setStatus(CompanyStatusEnums.ENABLE.getCode());
        company.setCreateBy(memberId);
        company.setMemberId(memberId);
        val saveFlag = companyService.save(company);
        return saveFlag ? company : null;
    }

//    private String generateQrCode(MemberAccount memberAccount) {
//        if (memberAccount == null) {
//            return "";
//        }
//        val qrCode = memberAccount.getQrCode();
//        if (StrUtil.isEmpty(qrCode)) {
//            val wxBar
//            createQrCode();
//        }
//    }

    public static void main(String[] args) {
        val a = "用户" + System.currentTimeMillis();
        log.info(a);
    }
}
